package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.nacos.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.mapper.ApCollectionMapper;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.FreemarkerService;
import com.heima.client.user.IUserClient;
import com.heima.common.exception.CustomException;
import com.heima.common.redis.CacheService;
import com.heima.model.common.dtos.*;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.common.pojos.article.*;
import com.heima.model.common.pojos.behavior.UnLikesBehavior;
import com.heima.model.common.pojos.wemedia.WmNewsComment;
import com.heima.model.common.vo.ApArticleVo;
import com.heima.utils.thread.ApThreadLocalUtil;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 陈辉
 * @data 2024 9:50
 */
@Service
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {

    @Autowired
    private ApArticleMapper apArticleMapper;
    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;
    @Autowired
    private ApArticleContentMapper apArticleContentMapper;
    @Autowired
    private FreemarkerService freemarkerService;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    @Autowired
    private ApCollectionMapper apCollectionMapper;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private IUserClient iUserClient;

    /**
     * 加载文章信息
     *
     * @param homeDto 查询条件
     * @param type    1：加载更多 2：加载更新
     * @return
     */
    @Override
    public ResponseResult load(ArticleHomeDto homeDto, int type) {
        //1. 参数校验
        if (homeDto == null || (type != 1 && type != 2)) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2. 数据处理
        if (homeDto.getMaxBehotTime() == null) homeDto.setMaxBehotTime(new Date());
        if (homeDto.getMinBehotTime() == null) homeDto.setMinBehotTime(new Date());
        if (homeDto.getSize() < 1 || homeDto.getSize() > 50) homeDto.setSize(10);

        //3. 调用mapper查询对应数据
        List<ApArticle> res = apArticleMapper.load(homeDto, type);

        //4. 封装结果返回
        return ResponseResult.okResult(res);
    }

    /**
     * 保存或修改文章信息
     *
     * @param articleDto
     * @return
     */
    @Override
    public ResponseResult saveOrUpdateArticle(ArticleDto articleDto) {
        //1. 同步自媒体文章信息
        if (articleDto.getId() == null) {
            //保存
            //1. 保存文章基本信息：ap_article
            ApArticle apArticle = new ApArticle();
            BeanUtils.copyProperties(articleDto, apArticle);
            apArticleMapper.insert(apArticle);
            articleDto.setId(apArticle.getId());

            //2. 创建文章默认配置：ap_article_config
            ApArticleConfig config = new ApArticleConfig(apArticle.getId());
            apArticleConfigMapper.insert(config);

            //3. 保存文章内容信息：ap_article_content
            ApArticleContent content = new ApArticleContent();
            content.setArticleId(apArticle.getId());
            content.setContent(articleDto.getContent());
            apArticleContentMapper.insert(content);
        } else {
            //修改
            //1. 修改文章基本信息：ap_article
            ApArticle apArticle = new ApArticle();
            BeanUtils.copyProperties(articleDto, apArticle);
            apArticleMapper.updateById(apArticle);

            //2. 修改文章内容信息：ap_article_content
            ApArticleContent content = new ApArticleContent();
            content.setArticleId(apArticle.getId());
            content.setContent(articleDto.getContent());
            //update ap_article_content set ? ,? where article_id = ?
            apArticleContentMapper.update(content,
                    Wrappers.<ApArticleContent>lambdaUpdate().eq(ApArticleContent::getArticleId, content.getArticleId()));
        }

        //2. 采用异步调用，提升当前接口的性能，生成文章详情页： freemarker + minio
        freemarkerService.generateDetailHtml(articleDto);


        //将文章id返回
        return ResponseResult.okResult(String.valueOf(articleDto.getId()));
    }


    /**
     * 文章上下架
     * <p>
     * is_down: 0: 上架， 1：下架
     *
     * @param map enable 0：下架， 1：上架
     */
    @Override
    public void downOrUp(Map map) {
        boolean isDown = true;            // 假设要下架

        if ((int) map.get("enable") == 1) {
            isDown = false;
        }
        ApArticleConfig config = new ApArticleConfig();
        //config.setArticleId((long) map.get("article_id"));
        //update ap_article_config set is_down = ? where article_id = ?
        apArticleConfigMapper.update(config,Wrappers.<ApArticleConfig>lambdaUpdate()
                .set(ApArticleConfig::getIsDown,isDown)
                .eq(ApArticleConfig::getArticleId,config.getArticleId()));

    }

    /**
     * 收藏文章
     *
     * @param collectionBehaviorDto
     * @return
     */
    @Override
    public ResponseResult collection(CollectionBehaviorDto collectionBehaviorDto) {
        //参数校验
        if (collectionBehaviorDto == null || collectionBehaviorDto.getEntryId() == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }
        Integer userId = ApThreadLocalUtil.get();
        //查询数据库总收藏量
        ApArticle article = getById(collectionBehaviorDto.getEntryId());
        Integer count = article.getCollection();
        if (count == null) {
            count = 0;
        }
        //1.判断用户操作类型 0 收藏 1 取消收藏
        if (collectionBehaviorDto.getOperation() == 0) {
            //1.1 收藏 判断是否收藏过 未收藏，直接添加到ap_collection数据库中
            ApCollection collection = apCollectionMapper.selectOne(Wrappers.<ApCollection>lambdaQuery()
                    .eq(ApCollection::getEntryId, userId)
                    .eq(ApCollection::getArticleId, collectionBehaviorDto.getEntryId()));
            if (collection != null) {
                throw new CustomException(AppHttpCodeEnum.COLLECTION_FAIL);
            }
            //1.2 保存到数据库ap_collection
            ApCollection apCollection = new ApCollection();
            apCollection.setEntryId(userId);
            apCollection.setArticleId(collectionBehaviorDto.getEntryId());
            apCollection.setPublishedTime(collectionBehaviorDto.getPublishedTime());
            apCollection.setCollectionTime(new Date());
            apCollection.setType((int) collectionBehaviorDto.getType());
            apCollectionMapper.insert(apCollection);
            //1.3 修改文章总的收藏数量 ap_article collection + 1
            update(Wrappers.<ApArticle>lambdaUpdate()
                    .set(ApArticle::getCollection, count + 1)
                    .eq(ApArticle::getId, collectionBehaviorDto.getEntryId()));

        } else {
            //2.取消收藏 将该用户收藏的文章从ap_collection中删除
            apCollectionMapper.delete(Wrappers.<ApCollection>lambdaQuery()
                    .eq(ApCollection::getEntryId, userId)
                    .eq(ApCollection::getArticleId, collectionBehaviorDto.getEntryId()));
            //2.1将收藏的总数 collection - 1
            if (count <= 0) {
                throw new CustomException(AppHttpCodeEnum.SERVER_ERROR);
            }
            update(Wrappers.<ApArticle>lambdaUpdate()
                    .set(ApArticle::getCollection, count - 1)
                    .eq(ApArticle::getId, collectionBehaviorDto.getEntryId()));
        }
        //3.将操作记录存入到Redis中
        Map<String, Object> map = new HashMap<>();
        map.put("data", collectionBehaviorDto);
        cacheService.hPut("collection_logs:" + userId, collectionBehaviorDto.getEntryId().toString()
                , JSON.toJSONString(map));
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 文章数据回显
     *
     * @param articleInfoDto
     * @return
     */
    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto articleInfoDto) {
        //参数校验
        if (articleInfoDto == null || articleInfoDto.getArticleId() == null
                || articleInfoDto.getAuthorId() == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }
        LoadArticleInfo articleInfo = new LoadArticleInfo();
        Integer userId = ApThreadLocalUtil.get();
        articleInfoDto.setUserId(userId);
        //1.判断用户是否关注了该文章的作者
        ResponseResult response = iUserClient.queryUserFollow(articleInfoDto);
        if (response.getData() != null){
            articleInfo.setIsfollow(true);
        }else {
            articleInfo.setIsfollow(false);
        }
        /*Object follow = cacheService.hGet("follow_logs:" + userId, articleInfoDto.getArticleId().toString());
        if (follow != null) {
            Map map = JSON.parseObject(follow.toString(), Map.class);
            UserRelationDto userRelationDto = JSONArray.parseObject(JSON.toJSONString(map.get("data")), UserRelationDto.class);
            articleInfo.setIsfollow(userRelationDto.getOperation() == 1 ? false : true);
        } else {
            articleInfo.setIsfollow(false);
        }*/
        //2.判断用户是否收藏了该文章
        Object json = cacheService.hGet("collection_logs:" + userId, articleInfoDto.getArticleId().toString());
        if (json != null) {
            Map map = JSON.parseObject(json.toString(), Map.class);
            CollectionBehaviorDto collection = JSONArray.parseObject(JSON.toJSONString(map.get("data")), CollectionBehaviorDto.class);
            articleInfo.setIscollection(collection.getOperation() == 1 ? false : true);
        } else {
            articleInfo.setIscollection(false);
        }
        //3.判断用户是否点赞了文章
        Object likeJson = cacheService.hGet("likes_logs:" + userId, articleInfoDto.getArticleId().toString());
        if (likeJson != null) {
            Map map = JSON.parseObject(likeJson.toString(), Map.class);
            LikesBehaviorDto likesBehavior = JSONArray.parseObject(JSON.toJSONString(map.get("data")), LikesBehaviorDto.class);
            articleInfo.setIslike(likesBehavior.getOperation() == 1 ? false : true);
        } else {
            articleInfo.setIslike(false);
        }
        //4.判断用户是否喜欢该文章
        Object unLikesJson = cacheService.hGet("un_likes_logs:" + userId, articleInfoDto.getArticleId().toString());
        if (unLikesJson != null) {
            UnLikesBehavior unLikesBehavior = JSON.parseObject(unLikesJson.toString(), UnLikesBehavior.class);
            articleInfo.setIsunlike(unLikesBehavior.getIsUnLike());
        } else {
            articleInfo.setIsunlike(false);
        }
        //返回数据
        return ResponseResult.okResult(articleInfo);
    }

    /**
     * 加载文章列表
     * @param dto
     * @return
     */
    @Override
    public ResponseResult getArticleList(CommentPageDto dto) {

        //设置查询分页page
        Page<ApArticle> page = new Page<>(dto.getPage(),dto.getSize());

        //设置查询条件
        LambdaQueryWrapper<ApArticle> wrapper = Wrappers.<ApArticle>lambdaQuery()
                .ge(ApArticle::getPublishTime, dto.getBeginDate())
                .le(ApArticle::getPublishTime, dto.getEndDate());

        apArticleMapper.selectPage(page,wrapper);

        return ResponseResult.okResult(page);
    }

    /**
     * 分页查询文章列表
     *
     * @param newsPageDto
     * @return
     */
    @Override
    public ResponseResult newsPage(NewsPageDto newsPageDto) {
        // 类型转换
//        Date beginDate = DateUtils.stringToDate(newsPageDto.getBeginDate());
//        Date endDate = DateUtils.stringToDate(newsPageDto.getEndDate());
        // 检查参数
        newsPageDto.checkParam();
        // 分页查询
        IPage<ApArticle> page = new Page(newsPageDto.getPage(), newsPageDto.getSize());
        LambdaQueryWrapper<ApArticle> lambdaQueryWrapper = Wrappers.<ApArticle>lambdaQuery()
                .eq(ApArticle::getAuthorId, newsPageDto.getWmUserId())
                .between(ApArticle::getPublishTime, newsPageDto.getBeginDate(), newsPageDto.getEndDate())
                .select(ApArticle::getAuthorId, ApArticle::getTitle, ApArticle::getLikes, ApArticle::getCollection
                        , ApArticle::getComment, ApArticle::getViews);

        lambdaQueryWrapper.orderByDesc(ApArticle::getPublishTime);

        page = page(page, lambdaQueryWrapper);

        PageResponseResult responseResult = new PageResponseResult(newsPageDto.getPage(), newsPageDto.getSize(), (int) page.getTotal());
        responseResult.setData(page.getRecords());

        //发送消息
        //kafkaTemplate.send("article_news", JSON.toJSONString(responseResult));

        return responseResult;
    }


    /**
     * 查询文章信息
     *
     * @return
     */
    @Override
    public String selectNews(CommentDto commentDto) {
        List<WmNewsComment> wmNewsComments= apArticleMapper.selectNews(commentDto.getBeginTime(),commentDto.getEndTime());
        return JSONArray.toJSONString(wmNewsComments);
    }


    /**
     * 清空评论数量
     *
     * @param commentDto
     */
    @Override
    public void deleteNews(CommentDto commentDto) {
        //将评论数量设置为0
        ApArticle apArticle = apArticleMapper.selectById(commentDto.getArticleId());
        apArticle.setComment(0);
        apArticleMapper.updateById(apArticle);
    }

    /**
     * 添加评论数量
     *
     * @param entryId
     */
    @Override
    public void saveComment(Long entryId) {
        //将评论数量加一
        ApArticle apArticle = apArticleMapper.selectById(entryId);
        Integer comment = apArticle.getComment();
        comment++;
        apArticle.setComment(comment);
        apArticleMapper.updateById(apArticle);
    }

    /**
     * 首页加载热点文章
     * @param homeDto
     * @param type
     * @return
     */
    @Override
    public ResponseResult loadHotArticle(ArticleHomeDto homeDto, int type) {
        //1. 参数校验
        if (homeDto == null || (type != 1 && type != 2)) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }
        //从redis中获取文章数据
        String result = cacheService.get("hot_article_" + homeDto.getTag());

        if (StringUtils.isNotEmpty(result)) {
            List<ApArticleVo> apArticleVos = JSONArray.parseArray(result, ApArticleVo.class);
            return ResponseResult.okResult(apArticleVos);
        }

        //缓存没有就查询数据库
        return load(homeDto,type);
    }

    /**
     * 热点文章计算
     */
    @Override
    public void computeHotArticle() {
        //1.查询近5天的文章数据
        Date dateTime = DateTime.now().minusDays(5).toDate();
        List<ApArticle> articles = apArticleMapper.load5Day(dateTime);

        if (articles == null || articles.size() == 0) {
            return;
        }

        //2.给文章算分
        List<ApArticleVo> articleVos = computeSort(articles);

        //3.计算每个频道的热点文章，取分值最高的前30篇文章，存入redis
        //3.1 提取这些文章的频道id
        List<Integer> channelIds = articleVos.stream().map(ApArticle::getChannelId).collect(Collectors.toList());
        //3.2 遍历频道
        for (Integer channelId : channelIds) {
            //收集每个频道的文章,并按分值排序：降序，降序后截取前30篇
            List<ApArticleVo> hotArticleInSameChannel  = articleVos.stream().filter(apArticleVo -> apArticleVo.getChannelId() == channelId)
                    .sorted(((o1, o2) -> o2.getSort() - o1.getSort()))
                    .limit(30)
                    .collect(Collectors.toList());

            //存入redis
            cacheService.set("hot_article_"+channelId,JSONArray.toJSONString(hotArticleInSameChannel));

        }

        //4.计算推荐频道的热点文章，存入redis
        List<ApArticleVo> hotArticleWithAllChannel  = articleVos.stream().sorted(((o1, o2) -> o2.getSort() - o1.getSort()))
                .limit(30)
                .collect(Collectors.toList());
        //存入redis
        cacheService.set("hot_article___all__",JSONArray.toJSONString(hotArticleWithAllChannel));

    }

    //热点文章算分
    private List<ApArticleVo> computeSort(List<ApArticle> articles) {

        List<ApArticleVo> apArticleVoList = new ArrayList<>();

        for (ApArticle article : articles) {
            Integer sort = 0;
            //获取文章阅读数量
            Integer views = article.getViews();
            if (views != null || views > 0) {
                sort += (views * 1);
            }

            //获取文章点赞数量
            Integer likes = article.getLikes();
            if (likes != null || likes > 0) {
                sort += (likes * 3);
            }

            //获取文章评论数量
            Integer comment = article.getComment();
            if (comment != null || comment > 0) {
                sort += (comment * 5);
            }

            //获取文章收藏数量
            Integer collection = article.getCollection();
            if (collection != null || collection > 0) {
                sort += (collection * 8);
            }

            ApArticleVo apArticleVo = new ApArticleVo();
            BeanUtils.copyProperties(article,apArticleVo);
            apArticleVo.setSort(sort);
            apArticleVoList.add(apArticleVo);
        }
        return apArticleVoList;
    }
}
